package cn.wizzer.app.crown.modules.services.impl.pl.ft;

import cn.wizzer.app.crown.commons.util.DateUtil;
import cn.wizzer.app.crown.commons.websocket.action.hg.ComplexPlayWayAction;
import cn.wizzer.app.crown.modules.models.CrownMatch;
import cn.wizzer.app.crown.modules.models.caches.CrownMatchSimplex;
import cn.wizzer.app.crown.modules.models.constant.CrownConstant;
import cn.wizzer.app.crown.modules.models.constant.CrownPattern;
import cn.wizzer.app.crown.modules.models.constant.CrownRedisConstant;
import cn.wizzer.app.crown.modules.models.enums.MatchTypeEnum;
import cn.wizzer.app.crown.modules.models.enums.PlayWayEnum;
import cn.wizzer.app.crown.modules.services.CrownConfigService;
import cn.wizzer.app.crown.modules.services.CrownMatchService;
import cn.wizzer.app.crown.modules.services.pl.ft.AllBetsResolverService;
import cn.wizzer.app.crown.modules.services.pl.ft.P3ResolverService;
import cn.wizzer.app.crown.modules.models.CrownConfig;
import cn.wizzer.app.match.modules.models.constant.Fields;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.util.JSONUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@IocBean(args = {"refer:dao"})
@Service(interfaceClass = P3ResolverService.class)
public class P3ResolverServiceImpl extends BaseServiceImpl<CrownMatch> implements P3ResolverService {
    private static final Log log = Logs.get();

    public P3ResolverServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private RedisService redisService;
    @Inject
    private CrownMatchService matchService;

    @Inject("allBetsResolverServiceImpl")
    private AllBetsResolverService allBetsService;

    @Inject
    @Reference(retries = 0, timeout = 500000)
    private CrownConfigService crownConfigService;

    private void crawler(String dateType, Integer pageNo, CrownConfig crownConfig) {

        new P3ResolverServiceImpl.PageAction() {
            @Override
            public void handle(List<JSONArray> list, String date) {
                Set<String> leagues = new HashSet<>();
                Deque<CrownMatch> queue = new LinkedList<>();

                //缓存league信息
                // "CROWN_BASKETBALL_P3_ALL_LEAGUE"
                String leagueKey = Fields.PREFIX_CROWN + MatchTypeEnum.FOOTBALL + Fields.KEY_JOIN + PlayWayEnum.P3 + Fields.ALL_STAGE + Fields.SUFFIX_LEAGUE;
                //缓存match信息
                // "CROWN_BASKETBALL_P3_ALL_MATCH"
                String matchKey = Fields.PREFIX_CROWN + MatchTypeEnum.FOOTBALL + Fields.KEY_JOIN + PlayWayEnum.P3 + Fields.ALL_STAGE + Fields.SUFFIX_MATCH;

                list.forEach(gm -> {
                    String[] dateTime = gm.getString(1).toLowerCase().split("<br>");
                    int dateTimeLength = dateTime.length;
                    CrownMatch match = new CrownMatch();
                    match.setType(MatchTypeEnum.FOOTBALL);
                    match.setStart(dateTimeLength == 3 ? dateTime[2] : "");
                    match.setDateTime(DateUtil.parseDate(date.substring(0, 5) + dateTime[0] + " " + (dateTimeLength >= 2 ? dateTime[1] + "m" : ""), DateUtil.DATE_FORMAT_PATTERN.Y_M_D_H_M_A.PATTERN));
                    match.setLeague(gm.getString(2));
                    // 根据时间区分‘今日’、‘早盘’,将值存入stage字段
                    match.setStage(dateTime[0].equals(date.substring(5, 10)) ? CrownConstant.BROWSE : CrownConstant.FUTURE);
                    String mbTeam = gm.getString(5);
                    // 去除主队名中的HTML标签
                    mbTeam = CrownPattern.HTML_PATTERN.matcher(mbTeam).replaceAll("");
                    if (mbTeam.contains("[中]")) {
                        mbTeam = mbTeam.replace("[中]", "").trim();
                        match.setHaveHome(false);
                    } else {
                        match.setHaveHome(true);
                    }
                    match.setMbTeam(mbTeam.trim());

                    // 去除客队名称HTML标签
                    String tgTeam = gm.getString(6).trim();
                    tgTeam = CrownPattern.HTML_PATTERN.matcher(tgTeam).replaceAll("");
                    match.setTgTeam(tgTeam);
                    match.setStrong(gm.getString(7));

                    CrownMatch old = matchService.queryByTeamInfo(match.getLeague(), match.getMbTeam(), match.getTgTeam(), match.getDateTime());
                    if (old != null) {
                        match.setId(old.getId());
                        match.setMid(old.getMid());
                    }

                    String redisKey = CrownRedisConstant.P3 + match.getId();
                    //计算no2
                    if (queue.size() == 0) {
                        // 爬取赛事的mid
                        match.setMid(gm.getString(0));
                        queue.add(match);
                        redisKey = saveMatchInfo(match, matchKey, leagues, redisKey, CrownRedisConstant.TIME_OUT_180_S);
                    } else {
                        CrownMatch last = queue.getLast();
                        if (last.getId().equals(match.getId())) {
                            queue.add(match);
                        } else {
                            queue.clear();
                            // 爬取赛事的mid
                            match.setMid(gm.getString(0));
                            queue.add(match);
                            redisKey = saveMatchInfo(match, matchKey, leagues, redisKey, CrownRedisConstant.TIME_OUT_180_S);
                        }
                    }

                    //set plays
                    CrownMatchSimplex simplex = new CrownMatchSimplex();
                    simplex.setMatchType(MatchTypeEnum.FOOTBALL);
                    simplex.setPlayWay(PlayWayEnum.P3);
                    simplex.setCrownMatchId(match.getId());
                    simplex.setNo2(queue.size());
                    //主队全场独赢胜率
                    simplex.setNo1(1);
                    simplex.setNo3(1);
                    simplex.setOdds(gm.getBigDecimal(17));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //客队全场独赢胜率
                    simplex.setNo3(2);
                    simplex.setOdds(gm.getBigDecimal(18));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //全场和局胜率
                    simplex.setNo3(3);
                    simplex.setOdds(gm.getBigDecimal(19));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //主队让球胜率
                    simplex.setNo1(2);
                    simplex.setNo3(1);
                    //全场让球系数
                    simplex.setCoeNum((gm.getString(7).equals("H") ? "" : "-") + gm.getString(8));
                    simplex.setOdds(gm.getBigDecimal(9));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //客队让球胜率
                    simplex.setNo3(2);
                    simplex.setCoeNum((gm.getString(7).equals("C") ? "" : "-") + gm.getString(8));
                    simplex.setOdds(gm.getBigDecimal(10));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //全场大胜率
                    simplex.setNo1(3);
                    simplex.setNo3(1);
                    //全场大系数
                    String o = gm.getString(11).replace("O", "");
                    simplex.setCoeNum(Strings.isEmpty(o) ? null : o);
                    simplex.setCoeWord("大");
                    simplex.setOdds(gm.getBigDecimal(13));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //全场小胜率
                    simplex.setNo3(2);
                    //全场小系数
                    String u = gm.getString(12).replace("U", "");
                    simplex.setCoeNum(Strings.isEmpty(u) ? null : u);
                    simplex.setCoeWord("小");
                    simplex.setOdds(gm.getBigDecimal(14));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //全场单胜率
                    simplex.setNo1(4);
                    simplex.setNo3(1);
                    simplex.setCoeNum(null);
                    simplex.setCoeWord("单");
                    simplex.setOdds(gm.getBigDecimal(15));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //全场双胜率
                    simplex.setNo3(2);
                    simplex.setCoeWord("双");
                    simplex.setOdds(gm.getBigDecimal(16));
                    compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    //滚球中可能半场已过。。
                    try {
                        //半场主队独赢胜率
                        simplex.setNo1(5);
                        simplex.setNo3(1);
                        simplex.setCoeWord(null);
                        simplex.setOdds(gm.getBigDecimal(29));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                        //半场客队独赢胜率
                        simplex.setNo3(2);
                        simplex.setOdds(gm.getBigDecimal(30));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                        //半场和局胜率
                        simplex.setNo3(3);
                        simplex.setOdds(gm.getBigDecimal(31));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);

                        //半场主队让球胜率
                        simplex.setNo1(6);
                        simplex.setNo3(1);
                        //半场让球系数
                        simplex.setCoeNum((gm.getString(21).equals("H") ? "" : "-") + gm.getString(22));
                        simplex.setOdds(gm.getBigDecimal(23));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                        //半场客队让球胜率
                        simplex.setNo3(2);
                        simplex.setCoeNum((gm.getString(21).equals("C") ? "" : "-") + gm.getString(22));
                        simplex.setOdds(gm.getBigDecimal(24));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);

                        //半场大
                        simplex.setNo1(7);
                        simplex.setNo3(1);
                        String o1 = gm.getString(25).replace("O", "");
                        simplex.setCoeNum(Strings.isEmpty(o1) ? null : o1);
                        simplex.setCoeWord("大");
                        simplex.setOdds(gm.getBigDecimal(28));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                        //半场小胜率
                        simplex.setNo3(2);
                        String u1 = gm.getString(26).replace("U", "");
                        simplex.setCoeNum(Strings.isEmpty(u1) ? null : u1);
                        simplex.setCoeWord("小");
                        //半场小系数
                        simplex.setOdds(gm.getBigDecimal(27));
                        compose(redisKey, simplex, CrownRedisConstant.TIME_OUT_180_S);
                    } catch (Exception ignore) {

                    }
                    // 比赛被订阅，爬所有玩法
                    if (ComplexPlayWayAction.instance().countSubscribe(match.getId()) > 0) {
                        // 爬虫
                        allBetsService.crawler(PlayWayEnum.P3, match, crownConfig);
                    }
                    // 推送整场比赛信息
                    ComplexPlayWayAction.instance().push(match.getId(), matchService.composeMatch(CrownRedisConstant.P3, match));
                });

                List<String> lrange = redisService.lrange(leagueKey, 0, -1);
                leagues.addAll(lrange);
                //缓存league信息
                redisService.del(leagueKey);
                leagues.forEach(league -> redisService.rpush(leagueKey, league));

                // 设置缓存有效时长
                redisService.expire(leagueKey, CrownRedisConstant.TIME_OUT_180_S);
            }

            private String saveMatchInfo(CrownMatch match, String matchKey, Set<String> leagues, String redisKey, int timeOut) {
                // 防止玩法重复插入
                if (null != match.getId()) {
                    redisService.del(redisKey);
                    matchService.insertOrUpdate(match);
                } else {
                    matchService.insert(match);
                    redisKey = CrownRedisConstant.P3 + match.getId();
                }
                leagues.add(match.getLeague());
                // redis缓存赛事信息并设置过期时间
                redisService.setex(matchKey + ":" + match.getId(), timeOut, JSONUtil.toJSONString(match));

                return redisKey;
            }
        }.page(dateType, pageNo, crownConfig);

    }


    /**
     * 组装单个比赛信息，推送时直接使用
     *
     * @param redisKey
     * @param simplex
     * @param timeOut
     */
    private void compose(String redisKey, CrownMatchSimplex simplex, Integer timeOut) {
        // 具体玩法信息存入redis，并设置过期时间
        redisService.setex(redisKey + ":" + simplex.getNo1() + ":" + simplex.getNo2() + ":" + simplex.getNo3(),
                timeOut,
                JSONObject.toJSONString(simplex));
    }


    @Override
    public void p3(CrownConfig crownConfig) {
        crawler(CrownConstant.BROWSE, 0, crownConfig);
    }

    public abstract class PageAction {

        public abstract void handle(List<JSONArray> list, String date);

        List<JSONArray> list = new ArrayList<>();

        int[] eleIdxs = new int[]{9, 10, 13, 14, 15, 16, 23, 24, 27, 38, 29, 30};

        public void page(String type, Integer pageNo, CrownConfig config) {
            while (true) {
                log.info("准备请求爬取足球综合过关玩法实时数据");
                String tailUrl = String.format("/app/member/FT_%s/body_var.php?uid=%s&rtype=%s&langx=%s&mtype=3" +
                                "&page_no=%d&g_date=ALL&league_id=&hot_game=&showgtype=FU&isie11=%%27N%%27",
                        type, config.getUid(), "p3", CrownConstant.LANGX, pageNo);
                String body = crownConfigService.getCrownData(tailUrl, config, CrownConstant.RE_TRY_TIMES);
                // 检测账号被登出
                if (crownConfigService.checkLogout(body, config)) {
                    // 重试登录(包括重试三次、自动请求新地址)
//                    crownConfigService.loginHg(config);
//                    // 如果还是登录失败 直接return结束 避免死循环
//                    if (config.isLogout()) {
//                        return;
//                    }
//                    // 登录成功则continue重新去请求
//                    continue;
                    return;
                }

                // body是空，监测到皇冠维护都return
                if (Strings.isBlank(body)) {
                    log.error("爬取足球P3失败，body为空");
                    return;
                } else if (crownConfigService.checkMaintain(body, config)) {
                    return;
                }

                // 解析数据
                // 页数
                Matcher m = CrownPattern.TOTAL_PAGE_NUM_PATTERN.matcher(body);
                Integer pg = 0;
                if (m.find()) {
                    pg = Integer.valueOf(m.group(0).replace(";", "").replace("t_page=", ""));
                    log.info("获取总页数：" + pg);
                } else {
                    log.error("爬取不到页数,即将开启维护.URL:" + tailUrl + "===body:" + body);
                    // 如果爬取不到总页数,调用监测接口
                    crownConfigService.checkMaintain(CrownConstant.MAINTAIN_INFO_SHORT, config);
                }
                m = CrownPattern.SYSTEM_DATE_PATTERN.matcher(body);
                String date = null;
                if (m.find()) {
                    date = m.group(0).replace(";", "").replace("top.today_gmt = ", "").replace("'", "");
                    log.info("获取系统日期：" + date);
                }

                // body_var 列表信息
                m = CrownPattern.G_METHOD_DATA_PATTERN.matcher(body);
                Pattern fr;
                Matcher fm;
                while (m.find()) {
                    String v = m.group();
                    v = v.substring(2, v.length() - 2);
                    JSONArray ds = JSONArray.parseArray(v);
                    fr = Pattern.compile("gm\\[\\'" + ds.get(0) + "(.*?)\\'\\];");
                    fm = fr.matcher(body);
                    String gms = "";
                    if (fm.find()) {
                        gms = fm.group();
                    }
                    JSONArray gm;
                    if (Strings.isNotBlank(gms)) {
                        gm = JSONArray.parseArray(gms.substring(7 + ds.get(0).toString().length(), gms.length() - 1));
                        gm.add(0, ds.get(0));
                        ds.remove(0);
                        gm.addAll(ds);
                    } else {
                        gm = ds;
                    }
                    // eleIndex 这些位置有些值为null,需要给填上默认值0
                    for (int i = 0; i < eleIdxs.length; i++) {
                        if (Strings.isEmpty(gm.getString(eleIdxs[i]))) {
                            gm.remove(eleIdxs[i]);
                            gm.add(eleIdxs[i], 0);
                        }
                    }
                    // P3不爬滚球中的数据。
                    // 目前滚球数据索引是53位，52是进行时间，目前有'MTIME^<font style=background-color=red>半场</font>'，也有'2H^37:10'。
                    // 非滚球数据索引只有46位。所以根据索引位数判断是否是滚球数据
                    if (gm.size() <= 46) {
                        list.add(gm);
                    }
                }

                if (pg <= ++pageNo) {
                    log.info("爬取成功，当前（ftp3）页面数据条数ftp3->pageNo:" + pageNo + ",pageSize:" + list.size());
                    handle(list, date);
                    return;
                }
            }
        }
    }
}
